#-*- coding:utf-8 -*-

import wx
import accomm
from types import *
from copy import copy, deepcopy

class Property(object):
    """
    """
    def __init__(self, name, mode, start, length, decoder, map = None):
        object.__init__(self)
        
        self.name = name
        self.map = map
        self.decoder = decoder
        self.start = start
        self.length = length
        self.mode = mode
        
        
    def setName(self, name):
        if not isinstance(name, StringType):
            raise TypeError
        
        self._name = name

    def getName(self):
        return self._name
    
    name = property(getName, setName, doc = 'Name attribute')
    
    
    def setMap(self, map):
        if not isinstance(map, DictType) and not isinstance(map, NoneType):
            raise TypeError
        if map is not None:
            for k, v in map.iteritems():
                if not isinstance(v, IntType):
                    raise ValueError
        
        self._map = map
        
    def getMap(self):
        return self._map
        
    map = property(getMap, setMap)

        
    def setDecoder(self, decoder):
        if not isinstance(decoder, DecoderBase):
            raise TypeError
        self._decoder = decoder
    
    def getDecoder(self):
        return self._decoder
    
    decoder = property(getDecoder, setDecoder)
 
    
    def setValue(self, value):
        if self._map is None:
            if isinstance(value, IntType):
                self._value = value
            else:
                raise TypeError
        elif value in self._map:
            self._value = value
        else:
            raise ValueError
        
    def getValue(self):
        return self._value
        
    value = property(getValue, setValue)

    
    def setStart(self, start):
        if not isinstance(start, TupleType):    raise TypeError
        if len(start) != 2:     raise ValueError
        
        self._start = start
        
    def getStart(self):
        return self._start
    
    start = property(getStart, setStart)
    
    def setLength(self, length):
        if not isinstance(length, TupleType):    raise TypeError
        if len(length) != 2:     raise ValueError
        
        self._length = length
    
    def getLength(self):
        return self._length
    
    length = property(getLength, setLength)    


    def decode(self, data):
        lenby, lenbi = self.length
        lenb = lenby * 8 + lenbi
        strby, strbi = self.start
 
        rel = 0
        while lenb > 0:
            val = data[strby] & (0xFF >> strbi)
            valLen = 8 - strbi
 
            if lenb < valLen:
                val = val >> (valLen - lenb)
                valLen = lenb
 
            rel = (rel << valLen) | val
 
            # Move position and length
            strby, strbi, lenb = strby + 1, 0, lenb - valLen
        
        self._value = self._decoder.decode(rel, self._map)
        return self._value
  
    
    def encode(self, data):
        """ return modified data given as parameter
        """
        lenby, lenbi = self.length
        lenb = lenby * 8 + lenbi
        strby, strbi = self.start

        tvalue = self._decoder.encode(self._value, self._map)
            
        while lenb > 0:
            valLen = min(8 - strbi, lenb)
            val = tvalue >> (lenb - valLen)
            
            data[strby] = data[strby] | val << (8 - valLen) - strbi
            
            tvalue = tvalue - (val << (lenb - valLen))
        
            # Move position and length
            strby, strbi, lenb = strby + 1, 0, lenb - valLen
    
        return data            
    
   

class DecoderBase(object):
    """ Singleton Pattern
    """
    _instance = None
    
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(DecoderBase, cls).__new__(cls, *args, **kwargs)
        return cls._instance
    
    """ """
    def decode(self, value, map = None):    raise NotImplementedError
    def encode(self, value, map = None):    raise NotImplementedError
    
class NormalSensorDecoder(DecoderBase):
    
    def decode(self, value, map = None):
        return value - 55
    
    def encode(self, value, map = None):
        return value + 55

class HPSensorDecoder(DecoderBase):
    
    def decode(self, value, map = None):
        return (value + 160) / 10.
        
    def encode(self, value, map = None):
        return value * 10 - 160


class LPSensorDecoder(DecoderBase):
    
    def decode(self, value, map = None):
        return value
    
    def encode(self, value, map = None):
        return value
    
class DictionaryDecoder(DecoderBase):
    def decode(self, value, map):
        for k, v in map.iteritems():
            if v == value:
                return k
        else:
            raise ValueError
    
    def encode(self, value, map):
        return map[value]
       
class DummyDecoder(DecoderBase):
    def decode(self, value):
        return value
    
    def encode(self, value):
        return value
            
  
    
class IndoorEditDlg(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self, parent, -1, "실내기 정보")
        
        hsiz = wx.BoxSizer(wx.HORIZONTAL)
        hsiz.AddSpacer(5)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.AddSpacer(5)
        infosiz = wx.GridBagSizer(5, 5)
        infosiz2 = wx.FlexGridSizer(2, 2)
        
        
        OperList = ['정지', '운전']
        rb = wx.RadioBox(self, -1, "운전상태", choices=OperList)
        infosiz.Add(rb, (1, 1))
        infosiz2.Add(rb)
        
        sizer.Add(infosiz2)

        butsiz = wx.BoxSizer(wx.HORIZONTAL)
        butsiz.Add(wx.Button(self, -1, "확인"))
        butsiz.Add(wx.Button(self, -1, "취소"))

        sizer.AddSpacer(5)
        sizer.Add(butsiz)
        
        
        hsiz.Add(sizer)
        
        self.SetSizer(hsiz)
        self.SetAutoLayout(True)


class Prototype(object):
    def __init__(self):
        self._objs = {}
        
    def registerObject(self, name, obj):
        """ register an object.
        """
        self._objs[name] = obj
        
    def unregisterObject(self, name):
        """ unregister an object
        """
        del self._objs[name]

    def getObject(self, name):
        return self._objs[name]
    
    def clone(self, name):
        obj = copy(self._objs[name])
        return obj
    
    def getAllObjects(self):
        return self._objs
    

class UnitPrototype(Prototype):
    def __init__(self):
        Prototype.__init__(self)
        self._intances = {}
        self.registerObject('Default Indoor', _DefaultIndoor())
        
    def create(self, name):
        unit = super(UnitPrototype, self).clone(name)
        unit.parent = super(UnitPrototype, self).getObject(name)
        if name not in self._intances:
            self._instances[name] = []
        self._instances[name].append(unit)
        
        return unit

    def getAllInstances(self, name):
        return self._instances[name]
    
    

class UnitManager(object):
    def __init__(self, connect):
        self.link = accomm.Linker()
        self.link.connect(connect)
        self.link.addListener(self.receiveHandler)
        
        self.units = {}

    def addUnit(self, unit):
        self.units.append(unit)
        
    def removeUnit(self, unit):
        for ul in self.units.values():
            if unit in ul:
                ul.remove(unit)

    def createIndoorUnit(self, address = -1):
        if address < 0:
            addr = -1
            for unit in self.units:
                if unit.getAddress() > addr:
                    addr = unit.getAddress()
            
            addr = addr + 1
        else:
            addr = address
            for unit in self.units:
                if unit.getAddress == address:
                    return False
                
        self.units.append(IndoorUnit(addr))
        
    def getUnits(self):
        return tuple(self.units)
        
    def receiveHandler(self, data):
        """ """
        
        srcaddr = data.pop(0)
        dstaddr = data.pop(0)
        mode = data.pop(0)
                
        for unit in self.units:
            if unit.getAddress() == dstaddr:
                unit.decodeComm(srcaddr, mode, data)
                
                if unit.hasTxComm():
                    (tx_dst_addr, tx_mode, tx_data) = unit.makeComm()
                    tx_data.insert(0, tx_mode)
                    tx_data.insert(0, tx_dst_addr)
                    tx_data.insert(0, unit.getAddress())
                    self.link.sendAC(tx_data)

    
                
                

class Unit(object):
    def __init__(self, address):
        self.address = address
        self.properties = {}
    
    def addProperty(self, property):
        mode = property.mode
        
        if not isinstance(mode, IntType):
            raise TypeError
        
        if mode > 0xFF:
            raise ValueError
        
        if mode not in self.properties:
            self.properties[mode] = []
        
        self.properties[mode].append(property)
        
        

class _DefaultIndoor(Unit):
    def __init__(self):
        Unit.__init__(self, 0)
        self.addProperty(Property('RoomTemp', 0x20, (1, 0), (1, 0), NormalSensorDecoder()))
        self.addProperty(Property('SetTemp', 0x20, (0, 0), (1, 0), NormalSensorDecoder()))
        self.addProperty(Property('Operation', 0x20,(4, 0), (0, 1), DictionaryDecoder(), {'On':1, 'Off':0}))
        self.addProperty(Property('Capa', 0x40,(5, 0), (1, 0), DummyDecoder()))


                


        
        
        
class DemoApp(wx.App):
    def OnInit(self):
        dlg = IndoorEditDlg(None)
        dlg.ShowModal()

        
        return False


if __name__ == "__main__":
    app = DemoApp(redirect=True)
    app.MainLoop()    